module('RpcUtils', package.seeall)

RpcRequest = class('RpcRequest')
RpcRequest.Timeout = 20
RpcRequest.NextRequestID = 1

function RpcRequest:ctor(method, requestContent)
    self.requestID = 0

    self.method = method
    self.requestContent = requestContent

    self.receiveResponse = false
    self.response = nil
    self.exceptionCode = nil
end

local RPCQueueLowerPriority = {}
local RPCQueue = {}
local RPCQueueHigherPriority = {}

local SEND_QUEUE_LIMIT = 20
local SendQueue = {}

NextForegroundRPC = nil

local function RefreshNextForegroundRPC()
    NextForegroundRPC = nil
    for _, request in ipairs(SendQueue) do
        if request.isBackground == false then
            NextForegroundRPC = request
            return
        end
    end

    for _, request in ipairs(RPCQueueHigherPriority) do
        if request.isBackground == false then
            NextForegroundRPC = request
            return
        end
    end

    for _, request in ipairs(RPCQueue) do
        if request.isBackground == false then
            NextForegroundRPC = request
            return
        end
    end

    if NextForegroundRPC then
        LoadingMaskViewModelInstance.reason.Value = 'RPC Request: ' .. NextForegroundRPC.method
    end
end


lastShowMask = false
local function ProcessMask()
    local showMask = false
    for _, request in ipairs(SendQueue) do
        if not request.isBackground then
            showMask = true
            break
        end
    end

    if not showMask then
        for _, request in ipairs(RPCQueue) do
            if not request.isBackground then
                showMask = true
                break
            end
        end
    end

    if not showMask then
        for _, request in ipairs(RPCQueueHigherPriority) do
            if not request.isBackground then
                showMask = true
                break
            end
        end
    end

    if showMask then
        VIEWM.showView(VIEW.LOADINGMASK)
    end
    LoadingMaskViewModelInstance.showMaskRPC.Value = showMask

    --print('%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%')
    --for _, request in ipairs(SendQueue) do
    --    print(request.method, request.isBackground)
    --end
    --for _, request in ipairs(RPCQueueHigherPriority) do
    --    print(request.method, request.isBackground)
    --end
    --for _, request in ipairs(RPCQueue) do
    --    print(request.method, request.isBackground)
    --end
    --print('=========================')

    lastShowMask = showMask
end

local function _callRpcMethodAsync(queue, method, content)
    isBackground = isBackground or false

    local rpcRequest = RpcRequest.new(method, content)
    table.insert(queue, rpcRequest)
    RefreshNextForegroundRPC()
    ProcessMask()

    local time = 0 --这个超时就是假的，安慰玩家用的，其实重连重发都是自动的
    while rpcRequest.receiveResponse == false do
        if NextForegroundRPC == rpcRequest then --只有这个请求是活跃请求才进行计时
            if time >= RpcRequest.Timeout then --超时等待重试
                local beginRetry = false

                if not rpcRequest.isBackground then
                    ShowErrorBoxA(LanModel.GetTextByID(500506) .. method, '', function(isConfirm)
                        beginRetry = true
                    end)
                    coroutine.waitUntil(function() return beginRetry == true end)
                else
                    coroutine.step()
                end
                --重发请求
                time = 0
            else
                coroutine.step()
                time = time + UnityEngine.Time.fixedDeltaTime
            end
        else
            coroutine.step()
        end
    end

    return rpcRequest.response, rpcRequest.exceptionCode
end

function callRpcMethodAsync(method, content)
    return _callRpcMethodAsync(RPCQueue, method, content)
end

function callRpcMethodHigherPriorityAsync(method, content)
    return _callRpcMethodAsync(RPCQueueHigherPriority, method, content)
end

function OnReceiveRpc(requestID, response)
    local index = table.findByFunc(SendQueue, function(a) return a.requestID == requestID end)
    local request = SendQueue[index]

    if request == nil then
        logError('Receive unknown rpc: ' .. requestID)
        return
    end

    table.remove(SendQueue, index)
    RefreshNextForegroundRPC()
    ProcessMask()

    request.receiveResponse = true
    request.response = response
    request.exceptionCode = ResponseCode.SUCCESS
end

function OnReceiveException(requestID, exceptionCode)
    local index = table.findByFunc(SendQueue, function(a) return a.requestID == requestID end)
    local request = SendQueue[index]

    if request == nil then
        logError('Receive unknown rpc errorcode', requestID)
        return
    end

    table.remove(SendQueue, index)
    RefreshNextForegroundRPC()
    ProcessMask()

    request.receiveResponse = true
    request.exceptionCode = exceptionCode
end

coroutine.start(function()
    while true do
        coroutine.waitUntil(function() return  #RPCQueue > 0 or #RPCQueueHigherPriority > 0 or #RPCQueueLowerPriority > 0 end)
        local currentRPC = nil

        if #RPCQueueHigherPriority > 0 then
            currentRPC = RPCQueueHigherPriority[1]
            table.remove(RPCQueueHigherPriority, 1)
        elseif #RPCQueue > 0 then
            currentRPC = RPCQueue[1]
            table.remove(RPCQueue, 1)
        else
            currentRPC = RPCQueueLowerPriority[1]
            table.remove(RPCQueueLowerPriority, 1)
        end
        currentRPC.requestID = RpcRequest.NextRequestID
        RpcRequest.NextRequestID = RpcRequest.NextRequestID + 1

        coroutine.waitUntil(function() return #SendQueue < SEND_QUEUE_LIMIT end)
        table.insert(SendQueue, currentRPC)
        RefreshNextForegroundRPC()
        ProcessMask()

        Channels.KcpChannel:Send(currentRPC.method, currentRPC.requestContent, currentRPC.requestID)
    end
end)